#include <windows.h>

#include "lib.h"

#include "pgconn.h"
#include "pggame.h"
#include "pgopts.h"
#include "pgsql.h"
#include "log.h"

#include "stdncerr.h"
#include "badpacketmsgs.h"

#include "PGXMLTranslator.h"

PGPokerConnection::PGPokerConnection(Server* server, SOCKET client)
	: Connection(server, client)
{
	game = (PGPokerGame*)0;

	minBet = 500;	// purposefully set maxBet < minBet to disallow playing
	maxBet = 100;	// games where these values don't come from database
	useCommission = true;
	compFactor = 0;

	loggedOn = false;

	casinoSpecific = (void*)0;

    // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
    msglen = 0;
    memset(msgbuf, 0, sizeof(msgbuf));

    #ifdef FORCEDSHOE
    m_pid="";
    #endif

}

PGPokerConnection::~PGPokerConnection()
{
	delete game;
	game = (PGPokerGame*)0;
	loggedOn = false;

	// CSN-7075: New Authentication
	trackLogoutSession();

	removeCasinoSpecific();
}

void
PGPokerConnection::gameDone()
{
	trackRemoveSavedGame();
	
	delete game;
	game = (PGPokerGame*)0;
}

void
PGPokerConnection::msgStart()
{
	msglen = 0;
}

void
PGPokerConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
PGPokerConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
PGPokerConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
PGPokerConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
PGPokerConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 1, msglen);
	msglen++;	// include msg len byte
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;
}

void
PGPokerConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

int32
PGPokerConnection::msgRemove(uint8* Src, int32* pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

int32
PGPokerConnection::msgRemove(uint8* Src, uint8* pResult)
{
	*pResult = *Src;

	return 1;
}

bool8
PGPokerConnection::getResponse()
{
	int32 ieat = 0, remain = iused, len;
	uint8* src = ibuf;
	int32 balance;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	while (remain > 0)
	{
		if (remain < src[0])
			break;

		// Handle an invalidly formatted message of length zero.
		if (src[0] <= 1)
		{
			ieat += 1;
			remain -= 1;
			src += 1;
			continue;
		}

        // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
	    msglen = 0;
	    memset(msgbuf, 0, sizeof(msgbuf));

		len = decode(src + 1, msgbuf, src[0] - 1);
		ieat += src[0];
		remain -= src[0];
		src += src[0];

		switch (msgbuf[0])
		{
			case 'L':
			{
				int32 err = 0;

				if (!parseCasinoLogin(5, &err))
				{
					msgStart();
					msgAdd((uint8)'E');
					msgAdd((uint8)(err & 0xff));
					msgEnd();
					break;
				}

				handleLogin(len);

				break;
			}

			case 'D':
			{
				if (!loggedOn)
				{
					Log::msgWarning("Deal: not logged on...invalid bet  ");

					trackaddBadPacket(TRY_DEAL_NOT_LOGGED_ON, msgbuf[0],(char*)msgbuf,len);

					msgStart();
					msgAdd((uint8)'B');
					msgEnd();

					break;
				}

				if (isServerStopping())
				{
					sendStopMsg();
					break;
				}

				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

				int32 i, bet;

				i = 1;
				i += msgRemove(msgbuf + i, &bet);

				trackGetBalance(&balance);

				if (game)
				{
					msgStart();
					msgAdd((uint8)'G');
					msgEnd();
				}
				else if (balance > 0 && balance >= bet &&
					bet >= minBet && bet <= maxBet)
				{
					game = new PGPokerGame(this, bet, useCommission);

					// balance is adjusted inside of trackNewGame
					//
					if (game && trackNewGame(bet, &balance, game->numCardsToDeal()) &&
						game->deal(balance))
					{
						trackSaveGame();
					}
					else
					{
						Log::msgError("  can't create game  ");

						delete game;
						game = (PGPokerGame*)0;

						msgStart();
						msgAdd((uint8)'N');
						msgEnd();
					}
				}
				else if (balance > 0)	// bad bet
				{
					Log::msgWarning("  invalid bet  ");

					msgStart();
					msgAdd((uint8)'B');
					msgEnd();
				}
				else					// not enough money in account
				{
					Log::msgWarning("  out of money  ");

					msgStart();
					msgAdd((uint8)'$');
					msgEnd();
				}
				break;
			}

			case 'S':	// Split
			{
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

				int32 i, twocard[2];
				uint8 which;

				i = 1;
				i += msgRemove(msgbuf + i, &which); twocard[0] = which;
				i += msgRemove(msgbuf + i, &which); twocard[1] = which;

				trackGetBalance(&balance);

				if (game)
					game->split(twocard,balance ); // sends errors, too
				else
				{
					trackaddBadPacket(TRY_SPLIT_WHEN_NO_GAME,msgbuf[0],(char*)msgbuf,msglen);
					msgStart();
					msgAdd((uint8)'N');
					msgEnd();
				}
				break;
			}

			case 'M':	//Unicode
			{
				switch (msgbuf[1])
				{
					case 'L':
					{
						int32 err = 0;

						if (!parseUnicodeCasinoLogin(6, &err))
						{
							msgStart();
							msgAdd((uint8)'E');
							msgAdd((uint8)(err & 0xff));
							msgEnd();
							break;
						}

						handleLogin(len);

						break;
					}
				}
				break;
			}

	#ifdef ALLOWOPTTEST	//todo: remove this
			case 't':	// test optimal player
			{
				extern void OptPlayerTest(uint8*, PGPokerConnection*);

				OptPlayerTest(msgbuf + 1, this);
				break;
			}
	#endif
            case 'm':  // Balance Request
            {
	            if (isServerStopping())
	            {
		            sendStopMsg();
		            break;
	            }

				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

	            //rule1: gotta be logged on
                if( loggedOn && trackGetBalance(&balance) )
                {
                    msgStart();
                    msgAdd((uint8)'m');
                    msgAdd((int32)balance);
                    msgEnd();
                }

	            break;
            }

			default:

				trackaddBadPacket(GETREPONSE_DEFAULT_CASE, msgbuf[0], (char*)msgbuf, len);

				Log::msgWarning("Connection::getResponse:  Unknown input"
					" (0x%02x)  ", msgbuf[0]);
				msgStart();
				msgAdd((uint8)'N');
				msgEnd();
				break;
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void
PGPokerConnection::handleLogin(int32 len)
{
	int32 balance;
	int32 err = 0;

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}

	if (!loggedOn)
	{
		PGPokerOpts opts;

		if (!trackHasSavedGame(&err) && err)
		{
			msgStart();
			msgAdd((uint8)'E');
			msgAdd((uint8)(err & 0xff));
			msgEnd();
			return;
		}

		if (!trackGetOptions(&opts) ||
			!trackGetPlayerLimits(&minBet, &maxBet, &compFactor) ||
			!trackGetBalance(&balance) 	)
		{
			msgStart();
			msgAdd((uint8)'N');
			msgEnd();
			return;
		}

		useCommission = opts.useCommission != 0;

		msgStart();
		msgAdd((uint8)'L');
		msgAdd(balance);
		msgAdd(minBet);
		msgAdd(maxBet);
		msgAdd((uint8)(game ? 1 : 0));
		if (game)
			game->send();
		msgEnd();

		loggedOn = true;
	}
	else
	{
		Log::msgWarning("Login: attempt to re-log on  ");

		trackaddBadPacket(TRY_LOGON_WHEN_LOGGED_ON,msgbuf[0],(char*)msgbuf,len);

		msgStart();
		msgAdd((uint8)'N');
		msgEnd();
	}
}

void	
PGPokerConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target =  new PGXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool PGPokerConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == PGPokerSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
